package comp353Games;

//imported packages
import java.util.*;

/**
 * ToadsAndFrogs.java describes a game of Toads and Frogs. More information
 * about this game is available here:
 * http://en.wikipedia.org/wiki/Toads_and_Frogs_%28game%29
 *
 * @author Kyle Burke, Tyler Radley, Andre Harvey, Deanna Fink
 * @version 4/30/12
 */
public class ToadsAndFrogs implements GameModel {

    // instance variables
    //Contents of the boxes.  0 is empty, 1 is a Toad, -1 is a Frog.
    private int[] boxes;
    /**
     * Number indicating an empty box
     */
    public static final int EMPTY = 0;
    /**
     * Number indicating a left move
     */
    public static final int LEFT = -1;
    /**
     * Number indicating a left move
     */
    public static final int RIGHT = 1;

    /**
     * Constructor for a default basic game with ten boxes
     */
    public ToadsAndFrogs() {
        // initialise instance variables

        this.boxes = new int[10];
        this.boxes[0] = EMPTY;
        this.boxes[1] = RIGHT;
        this.boxes[2] = EMPTY;
        this.boxes[3] = LEFT;
        this.boxes[4] = EMPTY;
        this.boxes[5] = RIGHT;
        this.boxes[6] = RIGHT;
        this.boxes[7] = EMPTY;
        this.boxes[8] = LEFT;
        this.boxes[9] = LEFT;
    }

    /**
     * Constructor for generic game.
     *
     * @param boxes Contents of the boxes.
     */
    public ToadsAndFrogs(int[] boxes) {
        this.boxes = new int[boxes.length];
        for (int i = 0; i < boxes.length; i++) {
            this.boxes[i] = boxes[i];
        }
    }

    /**
     * gives the user a copy of boxes without being able to mutate it
     */
    public int[] boxesClone() {
        int[] boxesClone = new int[boxes.length];
        for (int i = 0; i < boxes.length; i++) {
            boxesClone[i] = boxes[i];
        }
        return boxesClone;
    }

    @Override
    public String getName() {
        return "Toads and Frogs";
    }

    @Override
    public ToadsAndFrogs getNewGame() {
        return new ToadsAndFrogs();
    }

    @Override
    public ToadsAndFrogsDisplay getDisplay(GameModel game) {
        return new ToadsAndFrogsDisplay(game);
    }

    @Override
    public boolean equals(Object other) {
        if (!(other instanceof ToadsAndFrogs)) {
            return false;
        } else {
            ToadsAndFrogs otherGame = (ToadsAndFrogs) other;
            if (this.boxes.length != otherGame.boxes.length) {
                return false;
            }
            for (int i = 0; i < this.boxes.length; i++) {
                if (this.boxes[i] != otherGame.boxes[i]) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Get the different possible moves for a player.
     *
     * @param currentPlayerMovesRight equals false when checking for left moves
     * and true when checking for right moves
     * @return A Vector of ToadsAndFrogs consisting of all moves the player can
     * legally make.
     */
    public Vector<ToadsAndFrogs> getPositions(boolean currentPlayerMovesRight) {
        int leftOrRight;
        if (currentPlayerMovesRight) {
            leftOrRight = RIGHT;
        } else {
            leftOrRight = LEFT;
        }
        Vector<ToadsAndFrogs> positions = new Vector<ToadsAndFrogs>();
        int[] positionBoxes;  //need another variable to prevent aliasing

        for (int i = 0; i < this.boxes.length; i++) {
            //re-initialize to prevent aliasing.
            positionBoxes = new int[this.boxes.length];
            if (this.boxes[i] == leftOrRight && (i + leftOrRight < this.boxes.length && leftOrRight == RIGHT) || (leftOrRight == LEFT && i > 0)) {
                if (this.boxes[i + leftOrRight] == EMPTY) {
                    getPositionsHelper(leftOrRight, i, positions, positionBoxes);
                } else if (this.boxes[i + leftOrRight] == (-1 * leftOrRight) && this.boxes[i + 2 * leftOrRight] == EMPTY && ((i + 2 < this.boxes.length && leftOrRight == RIGHT) || (leftOrRight == LEFT && i > 1))) {
                    //create the new position
                    for (int j = 0; j < i - 1 + leftOrRight; j++) {
                        positionBoxes[j] = this.boxes[j];
                    }
                    positionBoxes[i] = EMPTY; //where the Toad moved from
                    positionBoxes[i + leftOrRight] = this.boxes[i + leftOrRight]; //the Frog it jumped
                    positionBoxes[i + (2 * leftOrRight)] = leftOrRight; //where the Toad moved to
                    for (int j = (i + 2 + leftOrRight); j < this.boxes.length; j++) {
                        positionBoxes[j] = this.boxes[j];
                    }
                    positions.add(new ToadsAndFrogs(positionBoxes));
                }
            }
        }
        return positions;
    }

    //  Helps the getPositions method.    
    private void getPositionsHelper(int leftOrRight, int index, Vector<ToadsAndFrogs> positions, int[] positionBoxes) {
        int difference = 0;
        if (leftOrRight == LEFT) {
            difference--;
        }
        for (int j = 0; j < index + difference; j++) {
            positionBoxes[j] = this.boxes[j];
        }
        positionBoxes[index + leftOrRight] = leftOrRight;
        positionBoxes[index] = EMPTY;
        for (int j = index + 2 + difference; j < this.boxes.length; j++) {
            positionBoxes[j] = this.boxes[j];
        }
        positions.add(new ToadsAndFrogs(positionBoxes));
    }

    @Override
    public int hashCode() {
        int code = 0;
        for (int i = 0; i < this.boxes.length; i++) {
            code += (this.boxes[i] + 1) * Math.pow(3, i);
        }
        return code;
    }

    @Override
    public boolean hasMovesRemaining(int playerNumber) {
        if (playerNumber > 2 || playerNumber < 1) {
            System.err.println("Invalid Player Number");
            return false;
        }
        for (int i = 0; i < boxes.length; i++) {
            if (!(this.updateGameHelper(i, playerNumber).equals(this))) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean validMove(int[] gamePress, int[] gameRelease, int[] gameClick, int playerNumber) {
        ToadsAndFrogs nextGame = updateGameHelper(gameClick[0], playerNumber);
        return !(this.equals(nextGame));
    }

    @Override
    public int[] mouseClickedHandler(int getX, int getY, int currentPlayer) {
        // Check if the click was inside the rectangle area.
        System.out.println(getX + "," + getY);
        int[] coordinates = new int[2];
        // Check if the click was inside the rectangle area.
        if (getY <= 46 && getY >= 17) {
            coordinates[0] = (int) Math.floor((getX - 17) / 30);
            coordinates[1] = (int) Math.floor((getX - 17) / 30);
            return coordinates;
        }
        coordinates[0] = -1;
        coordinates[1] = -1;
        return coordinates;
    }

    @Override
    public String save() {
        String saveText = "<ToadsAndFrogs>\n";
        saveText += "<boxes>\n";
        saveText += boxes.length + "x";
        for (int i = 0; i < boxes.length; i++) {
            saveText += this.boxes[i] + ".";
        }
        saveText += "\n</boxes>\n";
        saveText += "</ToadsAndFrogs>";
        return saveText;
    }

    @Override
    public ToadsAndFrogs load(String fileString) {
        int index = 22;

        String lengthString = "";
        while (fileString.substring(index, index + 1).equals("x") == false) {
            lengthString += fileString.charAt(index);
            index++;
        }
        index++;
        int lengthValue = Integer.parseInt(lengthString);
        int[] newBoard = new int[lengthValue];

        for (int i = 0; i < lengthValue; i++) {
            String integerHelper = "";
            while (fileString.substring(index, index + 1).equals(".") == false) {
                integerHelper += fileString.charAt(index);
                index++;
            }
            newBoard[i] = new Integer(integerHelper).intValue();
            index++;
        }
        return new ToadsAndFrogs(newBoard);
    }

    @Override
    public ToadsAndFrogs updateGame(int[] gamePress, int[] gameRelease, int[] gameClick, int playerNumber) {
        return updateGameHelper(gameClick[0], playerNumber);
    }

    /**
     * creates a new ToadsAndFrogs based on the move made
     *
     * @param boxIndex the index of the box selected for the attempted move
     * @param playerNumber number of the player
     * @return a new game or the same game if an invalid move is made
     *
     */
    public ToadsAndFrogs updateGameHelper(int boxIndex, int playerNumber) {
        if (boxIndex >= this.boxes.length) {
            return this;
        }
        int[] positionBoxes = new int[this.boxes.length];
        //copy the other array
        for (int i = 0; i < positionBoxes.length; i++) {
            positionBoxes[i] = this.boxes[i];
        }

        if (this.boxes[boxIndex] == EMPTY) {
            return this;
        } else if (this.boxes[boxIndex] == RIGHT && playerNumber == 2) {
            if (boxIndex == this.boxes.length - 1) {
                //Toads at the end can't jump
                return this;
            }
            if (this.boxes[boxIndex + 1] == EMPTY) {
                positionBoxes[boxIndex] = EMPTY;
                positionBoxes[boxIndex + 1] = RIGHT;
                return new ToadsAndFrogs(positionBoxes);
            } else if (boxIndex == this.boxes.length - 2) {
                //Toads can't jump at this point
                return this;
            } else if (this.boxes[boxIndex + 1] == LEFT && this.boxes[boxIndex + 2] == EMPTY) {
                positionBoxes[boxIndex] = EMPTY;
                positionBoxes[boxIndex + 2] = RIGHT;
                return new ToadsAndFrogs(positionBoxes);
            } else {
                //This toad cannot move.
            }
        } else if (this.boxes[boxIndex] == LEFT && playerNumber == 1) { //this.boxes[boxIndex] must equal -1
            if (boxIndex == 0) {
                //Frogs at the beginning have nowhere to jump to.
                return this;
            } else if (this.boxes[boxIndex - 1] == EMPTY) {
                positionBoxes[boxIndex] = EMPTY;
                positionBoxes[boxIndex - 1] = LEFT;
                return new ToadsAndFrogs(positionBoxes);
            } else if (boxIndex == 1) {
                //Frogs can't jump at this point
                return this;
            } else if (this.boxes[boxIndex - 1] == RIGHT && this.boxes[boxIndex - 2] == EMPTY) {
                positionBoxes[boxIndex] = EMPTY;
                positionBoxes[boxIndex - 2] = LEFT;
                return new ToadsAndFrogs(positionBoxes);
            }
        }
        return this;
    }

    //toString
    /**
     * Returns a string version of this.
     *
     * @param indent The indentation between the edge of the window and the
     * beginning of the text
     * @return //Returns a string version of the game with an indentation.
     *
     */
    public String toString(String indent) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(indent + "Toads and Frogs\n");
        stringBuilder.append(indent + "|");
        for (int i = 0; i < this.boxes.length; i++) {
            stringBuilder.append(" ");
            if (this.boxes[i] == LEFT) {
                stringBuilder.append("F");
            } else if (this.boxes[i] == EMPTY) {
                stringBuilder.append(" ");
            } else if (this.boxes[i] == RIGHT) {
                stringBuilder.append("T");
            } else {
                //this case should never happen.  If we reach this, something went logically wrong!
                stringBuilder.append(this.boxes[i]);
            }
            stringBuilder.append(" |");
        }
        stringBuilder.append("\n");
        return stringBuilder.toString();
    }

    @Override
    public String toString() {
        return this.toString("");
    }

    //implimentation purposes
    @Override
    public int[] mousePressedHandler(int xPosition, int yPosition) {
        return null;
    }

    //implimentation purposes
    @Override
    public int[] mouseReleasedHandler(int xPosition, int yPosition) {
        return null;
    }

    @Override
    public String[] getInstructions(int currentPlayer) {
        String[] instructions = new String[3];
        String direction;
        if (currentPlayer == 1) {
            instructions[2] = "Mr. FROG";
            direction = "left";
        } else {
            instructions[2] = "Ms. TOAD";
            direction = "right";
        }
        instructions[0] = "Welcome to Toads and Frogs!  ";
        instructions[1] = instructions[2] + ", you may hop " + direction + " to a blank space or hop over one opponent to a blank space.";

        return instructions;
    }

    /**
     * Unit Test!
     */
    public static void main(String[] args) {
        ToadsAndFrogs game0 = new ToadsAndFrogs();
        System.out.println(game0);
        System.out.println(" has positions: ");
        System.out.println("Left:");
        for (ToadsAndFrogs position : game0.getPositions(false)) {
            System.out.println(position);
        }
        System.out.println("Right:");
        for (ToadsAndFrogs position : game0.getPositions(true)) {
            System.out.println(position);
        }
        System.out.println("***********");
        ToadsAndFrogs game1 = new ToadsAndFrogs(new int[]{EMPTY, RIGHT, LEFT, LEFT, EMPTY, RIGHT, RIGHT, LEFT, EMPTY, LEFT});
        System.out.println(game1);
        System.out.println("Left: " + game1.hasMovesRemaining(LEFT));
        for (ToadsAndFrogs position : game1.getPositions(false)) {
            System.out.println(position);
        }
        System.out.println("Right: " + game1.hasMovesRemaining(RIGHT));
        for (ToadsAndFrogs position : game1.getPositions(true)) {
            System.out.println(position);
        }
    }
}
